/* tolua: functions to push C values.
** Support code for Lua bindings.
** Written by Waldemar Celes
** TeCGraf/PUC-Rio
** Apr 2003
** $Id: $
*/

/* This code is free software; you can redistribute it and/or modify it.
** The software provided hereunder is on an "as is" basis, and
** the author has no obligation to provide maintenance, support, updates,
** enhancements, or modifications.
*/

#include "tolua++.h"
#include "lauxlib.h"

#include <stdlib.h>

TOLUA_API void tolua_pushvalue( lua_State* L, int lo ) {
    lua_pushvalue( L, lo );
}

TOLUA_API void tolua_pushboolean( lua_State* L, int value ) {
    lua_pushboolean( L, value );
}

TOLUA_API void tolua_pushnumber( lua_State* L, lua_Number value ) {
    lua_pushnumber( L, value );
}

TOLUA_API void tolua_pushstring( lua_State* L, const char* value ) {
    if ( value == NULL ) {
        lua_pushnil( L );
    }
    else {
        lua_pushstring( L, value );
    }
}

TOLUA_API void tolua_pushlstring(lua_State* L, const char* value, size_t len) {
    if (value == NULL) {
        lua_pushnil(L);
    }
    else {
        lua_pushlstring(L, value, len);
    }
}

TOLUA_API void tolua_pushuserdata( lua_State* L, void* value ) {
    if ( value == NULL ) {
        lua_pushnil( L );
    }
    else {
        lua_pushlightuserdata( L, value );
    }
}

TOLUA_API void tolua_pushusertype( lua_State* L, void* value,
                                   const char* type ) {
    if ( value == NULL ) {
        lua_pushnil( L );
    }
    else {
        luaL_getmetatable( L, type );
        lua_pushstring( L, "tolua_ubox" );
        lua_rawget( L, -2 );     /* stack: mt ubox */

        if ( lua_isnil( L, -1 ) ) {
            lua_pop( L, 1 );
            lua_pushstring( L, "tolua_ubox" );
            lua_rawget( L, LUA_REGISTRYINDEX );
        };

        lua_pushlightuserdata( L, value );

        lua_rawget( L, -2 );                    /* stack: mt ubox ubox[u] */

        if ( lua_isnil( L, -1 ) ) {
            lua_pop( L, 1 );                       /* stack: mt ubox */
            lua_pushlightuserdata( L, value );
            *( void** )lua_newuserdata( L,
                                        sizeof( void* ) ) = value; /* stack: mt ubox u newud */
            lua_pushvalue( L, -1 );                /* stack: mt ubox u newud newud */
            lua_insert( L, -4 );                   /* stack: mt newud ubox u newud */
            lua_rawset( L, -3 );                   /* stack: mt newud ubox */
            lua_pop( L, 1 );                       /* stack: mt newud */
            /*luaL_getmetatable(L,type);*/
            lua_pushvalue( L, -2 );          /* stack: mt newud mt */
            lua_setmetatable( L, -2 );       /* stack: mt newud */
#ifdef LUA_VERSION_NUM
            lua_pushvalue( L, TOLUA_NOPEER );
#if LUA_VERSION_NUM > 501
            lua_setuservalue( L, -2 );
#else
            lua_setfenv( L, -2 );
#endif
#endif
        }
        else {
            /* check the need of updating the metatable to a more specialized class */
            lua_insert( L, -2 );                    /* stack: mt ubox[u] ubox */
            lua_pop( L, 1 );                        /* stack: mt ubox[u] */
            lua_pushstring( L, "tolua_super" );
            lua_rawget( L, LUA_REGISTRYINDEX );     /* stack: mt ubox[u] super */
            lua_getmetatable( L, -2 );              /* stack: mt ubox[u] super mt */
            lua_rawget( L, -2 );                    /* stack: mt ubox[u] super super[mt] */

            if ( lua_istable( L, -1 ) ) {
                lua_pushstring( L,
                                type );              /* stack: mt ubox[u] super super[mt] type */
                lua_rawget( L,
                            -2 );                    /* stack: mt ubox[u] super super[mt] flag */

                if ( lua_toboolean( L, -1 ) == 1 ) { /* if true */
                    lua_pop( L, 3 ); /* mt ubox[u]*/
                    lua_remove( L, -2 );
                    return;
                }
            }

            /* type represents a more specilized type */
            /*luaL_getmetatable(L,type);             // stack: mt ubox[u] super super[mt] flag mt */
            lua_pushvalue( L,
                           -5 );                 /* stack: mt ubox[u] super super[mt] flag mt */
            lua_setmetatable( L,
                              -5 );             /* stack: mt ubox[u] super super[mt] flag */
            lua_pop( L, 3 );                       /* stack: mt ubox[u] */
        }

        lua_remove( L, -2 );  /* stack: ubox[u]*/
    }
}

TOLUA_API void tolua_pushusertype_and_takeownership( lua_State* L, void* value,
        const char* type ) {
    tolua_pushusertype( L, value, type );
    tolua_register_gc( L, lua_gettop( L ) );
}

TOLUA_API void tolua_pushfieldvalue( lua_State* L, int lo, int index, int v ) {
    lua_pushnumber( L, index );
    lua_pushvalue( L, v );
    lua_settable( L, lo );
}

TOLUA_API void tolua_pushfieldboolean( lua_State* L, int lo, int index,
                                       int v ) {
    lua_pushnumber( L, index );
    lua_pushboolean( L, v );
    lua_settable( L, lo );
}


TOLUA_API void tolua_pushfieldnumber( lua_State* L, int lo, int index,
                                      lua_Number v ) {
    lua_pushnumber( L, index );
    tolua_pushnumber( L, v );
    lua_settable( L, lo );
}

TOLUA_API void tolua_pushfieldstring( lua_State* L, int lo, int index,
                                      const char* v ) {
    lua_pushnumber( L, index );
    tolua_pushstring( L, v );
    lua_settable( L, lo );
}

TOLUA_API void tolua_pushfielduserdata( lua_State* L, int lo, int index,
                                        void* v ) {
    lua_pushnumber( L, index );
    tolua_pushuserdata( L, v );
    lua_settable( L, lo );
}

TOLUA_API void tolua_pushfieldusertype( lua_State* L, int lo, int index,
                                        void* v, const char* type ) {
    lua_pushnumber( L, index );
    tolua_pushusertype( L, v, type );
    lua_settable( L, lo );
}

TOLUA_API void tolua_pushfieldusertype_and_takeownership( lua_State* L, int lo,
        int index, void* v, const char* type ) {
    lua_pushnumber( L, index );
    tolua_pushusertype( L, v, type );
    tolua_register_gc( L, lua_gettop( L ) );
    lua_settable( L, lo );
}

